/*
** server.c - TFTP server that implements RRQ requests
*/
#include <stdlib.h> /* for exit,EXIT_FAILURE */
#include <unistd.h> /* for getpid,close,fork */
#include <errno.h> /* for errno */
#include <sys/wait.h> /* for waitpid */
#include <netdb.h> /* for sockadder_in,sockadder */
#include <string.h> /* for memset */

#include "./logger/logger.h"
#include "./clientList/clientList.h"
#include "clientHandler.h"
#include "serverDefines.h"

#define MAXBUFLEN 100
#define ZERO 0

int serverSocket; //incoming requests socket

int activeServer=TRUE;

/**
 * author: Assaf Paz.
 * 
 * handleShutdown
 * 
 * s - signal number
 * 
 * close the server in a mannerly fashion. free all alocated data, close open streams
 * and shutdown the logger.
 */
void handleShutdown(int s)
{
	activeServer = FALSE;
}

/**
 * author: Assaf Paz.
 * 
 * handleDeadClient
 * 
 * s - signal number
 * 
 * handle the immature death of a child process. remove it from the container and
 * close it's stream.
 */
void handleDeadClient(int s)
{
    logMessage("handleDeadClient: Child with process # %d has died unnaturally\n",getpid());
    exit(1);
}

/**
 * author: Assaf Paz.
 * 
 * setSigChild
 * 
 * n - signal number
 * 
 * handle collect dead process childs
 */
void setSigChild(int n)
{
    while(waitpid(-1, NULL, WNOHANG) > 0)
        ;
    logMessage("setSigChild: sigcatch\n");
}

/**
 * author: Assaf Paz.
 * 
 * main
 * 
 * argc - number of arguments
 * 
 * argv - argument string
 * 
 * main will accept any of the following command line argumets (or none at all):
 * 				-RTO n			total time before giving up
 * 				-RTT n			time between retries
 * 				-PORT n     	server port number
 * 				-MAX_CLIENTS	the maximum amount of simoultaneus clients
 * 
 * Bad arguments are ignored by default. 
 */
int main(int argc,char *argv[])
{
    struct sockaddr_in localAddr; // my address information
    struct sockaddr_in remoteAddr; // connectors address information
    socklen_t addrLen;
    int numbytes;
    int pid;
    char buf[MAXBUFLEN];
    
    setSignalHandler(SIGCHLD,&handleShutdown);
    
    initiateLogger();
    
    initializeServerConfiguration(argc, argv);
    initializeClientList();

    if (-1 == (serverSocket = socket(PF_INET, SOCK_DGRAM, 0)))
    {
        perror("socket");
        logMessage("main: socket error\n");
        exit(1);
    }

    localAddr.sin_family = AF_INET; // host byte order
    localAddr.sin_port = htons(getPort()); // short, network byte order
    localAddr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
    memset(&(localAddr.sin_zero), '\0', 8); // zero the rest of the struct

    if (bind(serverSocket, (struct sockaddr *)&localAddr,
             sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        logMessage("main: bind error\n");
        exit(1);
    }
	logMessage("main: Server has been fully initiated, waiting for incoming requests...\n");
    setSignalHandler(SIGINT,&handleShutdown);
    setSignalHandler(SIGTERM,&handleShutdown);
    setSignalHandler(SIGHUP,&handleShutdown);
    setSignalHandler(SIGCHLD,&setSigChild);

    addrLen = sizeof(struct sockaddr);
    while (activeServer == TRUE)
    {		        
    	while(waitpid(-1, NULL, WNOHANG) > 0)
	    	;
		
        if ((numbytes=recvfrom(serverSocket, buf, MAXBUFLEN-1 , 0,
                               (struct sockaddr *)&remoteAddr, &addrLen)) == -1)
        {
        	if (errno == EINTR)
           	{
           		continue;
           	}
            perror("recvfrom");
            logMessage("main: recvfrom error\n");
	        exit(1);
        }
        while(waitpid(-1, NULL, WNOHANG) > 0)
            ;
        if((pid=fork())==0)
        {
            setSignalHandler(SIGINT,&handleDeadClient);
            setSignalHandler(SIGTERM,&handleDeadClient);
            setSignalHandler(SIGHUP,&handleDeadClient);
            setSignalHandler(SIGPIPE,&handleDeadClient);
	        buf[numbytes] = '\0';
	       	close(serverSocket);
			acceptRequest(buf, numbytes, &remoteAddr);
			request_t request;
	        examineRequest(&request,&remoteAddr,(void*)buf);
	        executeRequest(&request);
	        exit(0);
    	}
	}
	//handle server shutdown
    setSignalHandler(SIGINT, SIG_IGN);
    setSignalHandler(SIGTERM, SIG_IGN);
    setSignalHandler(SIGHUP, SIG_IGN);
    setSignalHandler(SIGPIPE, SIG_IGN);
    clearClientList();
    logMessage("main: Server has shutdown successfully\n");
    closeLogger();
    return 0;
}

